module HplAssets.Hephaestus (
  --HephaestusModel(..),  -- this definition was moved to module HplAssets/Hephaestus/Types.hs created similarly to modules Types of the assets bpm and ucm.
  HephaestusTransformation(..),
  transformHpl,
  emptyHpl
) where

import HplAssets.Hephaestus.Transformation -- factored out to avoid cyclic dependencies
import HplAssets.Hephaestus.MetaProgramming
import HplAssets.Hephaestus.MetaDataTypes
import HplAssets.Hephaestus.MetaData
import HplAssets.Hephaestus.Types
import FeatureModel.Types
import Language.Haskell.Syntax
import Prelude hiding (lookup)
import Data.Map (lookup)
-- *******************************************************
import HplProducts.HephaestusTypes -- where is defined the data types SPLModel and InstanceModel
-- *******************************************************

--data HephaestusModel = HephaestusModel [HsModule]

-- This function transformHplOld generates correctly a instance of Hephaestus (modules Test.hs and TestTypes.hs).
-- But, this function does not generate correctly the own instance of Hephaestus (modules Hephaestus.hs e HephaestusTypes.hs). 
-- Then, we broke the definition of the transformation BindProductName into BindProductNameM1 and BindProductNameM2. See function below transformHpl.
transformHplOld :: HephaestusTransformation -> HephaestusModel -> HephaestusModel -> HephaestusModel
transformHplOld t (HephaestusModel [emptyProduct1, emptyProduct2]) (HephaestusModel modules)  =  HephaestusModel module'
 where
  module' = transformHpl' t modules
  transformHpl' SelectEmptyProduct [] = [selectEmptyProductM1 emptyProduct1, selectEmptyProductM2 emptyProduct2]
  transformHpl' (SelectAsset a) (m:mx) = [selectAssetM1 a m, selectAssetM2 a (head mx)]
--  transformHpl' (BindProductName n) (m:mx) = [bindProductName n m, bindProductName n (head mx)]
  transformHpl' t _ = error ("Transformation " ++ show t ++ " not applicable.")


-- adapted into generation of the own Hephaestus instance using data types SPLModel and InstanceModel in separated module
transformHpl :: HephaestusTransformation -> SPLModel -> InstanceModel -> InstanceModel
transformHpl t (SPLModel (HephaestusModel [emptyProduct1, emptyProduct2])) (InstanceModel fc (HephaestusModel modules))  =  InstanceModel fc (HephaestusModel module')
 where
  module' = transformHpl' t modules
  transformHpl' SelectEmptyProduct [] = [selectEmptyProductM1 emptyProduct1, selectEmptyProductM2 emptyProduct2]
  transformHpl' (SelectAsset a) (m:mx) = [selectAssetM1 a m, selectAssetM2 a (head mx)]
--  transformHpl' (BindProductName n) (m:mx) = [bindProductName n m, bindProductName n (head mx)]
  transformHpl' (BindProductNameM1 n) (m:mx) = [bindProductName n m, (head mx)]
  transformHpl' (BindProductNameM2 n) (m:mx) = [m, bindProductName n (head mx)]
  transformHpl' t _ = error ("Transformation " ++ show t ++ " not applicable.")  


emptyHpl :: HephaestusModel
emptyHpl = HephaestusModel []


-- ----------------------------------------------------------
-- Transformations
-- ----------------------------------------------------------

--
-- Rename module name
--  from HplProducts.Empty 
--    to HplProducts.Test
--
selectEmptyProductM1::HsModule -> HsModule
selectEmptyProductM1 = bindProductName "Test" 

selectEmptyProductM2::HsModule -> HsModule
selectEmptyProductM2 = bindProductName "TestTypes" 

--
-- Set product name explicitly.
--
bindProductName :: String -> HsModule -> HsModule
bindProductName n = setModuleName ("HplProducts." ++ n)

--
-- Select a specific asset.
-- Carry out all metaprogramming-based transformations.
-- Use metadata about the assets to this end.
--

-- M1 = main module of the Hephaestus's instance. For exemple, Test.hs, Hephaestus.hs 
selectAssetM1 :: String -> HsModule -> HsModule
selectAssetM1 n
  = addConstructor "TransformationModel" xtype
  . addUpdateCase "transform" xfun xtype [sel'] sel
  . initializeField "InstanceModel" sel empty
--  . initializeField "SPLModel" sel' empty   -- Lucineia: it doesn't need, because it does nothing in Hephaestus instance
  . addImportDecl ("HplAssets." ++ mod)
  . addImportDecl ("HplProducts." ++ modtype')
 where
  metadata = maybe (error ("Missing metadata for " ++ n ++ ".")) id $ lookup n assetMetaData
  mod    = assetModule metadata
  modtype' = assetModuleType' metadata
  sel'   = assetSelector' metadata
  sel    = assetSelector metadata
  empty  = assetEmpty metadata
  xfun   = assetXFun metadata
  xtype  = assetXType metadata

-- M2 = this module contains the data types SPLModel and InstanceModel of the Hephaestus's instance
selectAssetM2 :: String -> HsModule -> HsModule
selectAssetM2 n
  = addField "InstanceModel" sel model
  . addField "SPLModel" sel' model
  . addImportDecl ("HplAssets." ++ modtype)
  where
  metadata = maybe (error ("Missing metadata for " ++ n ++ ".")) id $ lookup n assetMetaData
  modtype  = assetModuleType metadata
  model    = assetModel metadata
  sel'     = assetSelector' metadata
  sel      = assetSelector metadata


-- This function is not been used! It was broken into selectAssetM1 and selectAssetM2, according source codes above
selectAsset :: String -> HsModule -> HsModule
selectAsset n
  = addUpdateCase "transform" xfun xtype [sel'] sel
  . addConstructor "TransformationModel" xtype
  . initializeField "InstanceModel" sel empty
  . addField "InstanceModel" sel model
  . initializeField "SPLModel" sel' empty
  . addField "SPLModel" sel' model
  . addImportDecl ("HplAssets." ++ mod)
  . addImportDecl ("HplAssets." ++ modtype)
 where
  metadata = maybe (error ("Missing metadata for " ++ n ++ ".")) id $ lookup n assetMetaData
  modtype= assetModuleType metadata
  mod    = assetModule metadata
  model  = assetModel metadata
  sel'   = assetSelector' metadata
  sel    = assetSelector metadata
  empty  = assetEmpty metadata
  xfun   = assetXFun metadata
  xtype  = assetXType metadata


-- ----------------------------------------------------------
-- I/O
-- ----------------------------------------------------------
